package cn.aws360.boot.system.biz.service.impl;

import cn.aws360.boot.base.api.constant.enums.DelFlagEnum;
import cn.aws360.boot.base.api.exception.ServiceException;
import cn.aws360.boot.base.api.utils.ServletUtils;
import cn.aws360.boot.data.api.factory.PageFactory;
import cn.aws360.boot.data.api.pojo.request.PageQuery;
import cn.aws360.boot.data.api.pojo.response.PageResult;
import cn.aws360.boot.data.api.utils.PojoUtils;
import cn.aws360.boot.system.api.constant.SysConstant;
import cn.aws360.boot.system.api.constant.enums.BucketEnum;
import cn.aws360.boot.system.api.exception.SysExceptionEnum;
import cn.aws360.boot.system.api.pojo.request.SysFileQuery;
import cn.aws360.boot.system.api.pojo.request.SysFileWrite;
import cn.aws360.boot.system.api.pojo.response.SysFileModel;
import cn.aws360.boot.system.biz.domain.SysFile;
import cn.aws360.boot.system.biz.helper.OssHelper;
import cn.aws360.boot.system.biz.mapper.SysFileMapper;
import cn.aws360.boot.system.biz.service.SysFileService;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.io.FileTypeUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.util.List;

/**
 * @author junnan
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class SysFileServiceImpl extends ServiceImpl<SysFileMapper, SysFile> implements SysFileService {

    private final OssHelper ossHelper;

    @Override
    @Transactional(rollbackFor = Exception.class)
    @CacheEvict(value = SysConstant.MODULE_FILE_CACHE, allEntries = true)
    public Boolean add(SysFileWrite sysFileWrite) {
        List<SysFileModel> fileModelList = sysFileWrite.getFile();
        List<SysFile> sysFileList = PojoUtils.copyToList(fileModelList, SysFile.class);
        for (SysFile sysFile : sysFileList) {
            if (BucketEnum.PRIVATE.getName().equals(sysFile.getBucket())) {
                String filePath = sysFile.getFilePath();
                sysFile.setFilePath(StrUtil.splitTrim(filePath, "?").get(0));
            }
        }
        return this.saveBatch(sysFileList);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @CacheEvict(value = SysConstant.MODULE_FILE_CACHE, allEntries = true)
    public Boolean remove(String ids) {
        List<Integer> fileIdList = Convert.toList(Integer.class, ids.split(StrUtil.COMMA));
        return this.removeByIds(fileIdList);
    }

    @Override
    public SysFileModel detail(Integer id) {
        SysFile sysFile = this.getSysFileById(id);
        return BeanUtil.copyProperties(sysFile, SysFileModel.class);
    }

    @Override
    public List<SysFileModel> findList(SysFileQuery sysFileQuery) {
        LambdaQueryWrapper<SysFile> queryWrapper = createQueryWrapper(sysFileQuery);
        List<SysFile> sysFileList = this.list(queryWrapper);
        return PojoUtils.copyToList(sysFileList, SysFileModel.class);
    }

    @Override
    public PageResult<SysFileModel> findPage(SysFileQuery sysFileQuery, PageQuery pageQuery) {
        LambdaQueryWrapper<SysFile> queryWrapper = createQueryWrapper(sysFileQuery);
        PageResult<SysFile> sysFilePage = PageFactory.getPageResult(this, pageQuery, queryWrapper);
        return PojoUtils.copyToPage(sysFilePage, SysFileModel.class);
    }

    @Override
    public SysFileModel upload(String bucket, String directory, File file) {
        String original = file.getName();
        try {
            byte[] bytes = FileUtil.readBytes(file);
            String fileType = FileUtil.getType(file);
            return uploadToModel(bucket, directory, original, fileType, bytes);
        } catch (Exception e) {
            throw new ServiceException(SysExceptionEnum.FILE_UPLOAD_FAIL, e);
        }
    }

    @Override
    public SysFileModel upload(String bucket, String directory, MultipartFile multipartFile) {
        String original = multipartFile.getOriginalFilename();
        try {
            byte[] bytes = multipartFile.getBytes();
            String fileType = FileTypeUtil.getType(multipartFile.getInputStream());
            return uploadToModel(bucket, directory, original, fileType, bytes);
        } catch (Exception e) {
            throw new ServiceException(SysExceptionEnum.FILE_UPLOAD_FAIL, e);
        }
    }

    @Override
    public void download(Integer id) {
        SysFileModel sysFileModel = detail(id);
        String directory = sysFileModel.getDirectory();
        String fileName = sysFileModel.getFileName();
        String objectName = directory + File.separator + fileName;
        byte[] bytes = ossHelper.getObject(sysFileModel.getBucket(), objectName);
        ServletUtils.renderFile(sysFileModel.getOriginal(), bytes);
    }

    private SysFile getSysFileById(Integer id) {
        SysFile sysFile = this.getById(id);
        checkNotExist(sysFile);
        return sysFile;
    }

    private LambdaQueryWrapper<SysFile> createQueryWrapper(SysFileQuery fileQuery) {
        return Wrappers.<SysFile>lambdaQuery()
                .like(ObjectUtil.isNotEmpty(fileQuery.getBucket()), SysFile::getBucket, fileQuery.getBucket())
                .like(ObjectUtil.isNotEmpty(fileQuery.getDirectory()), SysFile::getDirectory, fileQuery.getDirectory())
                .eq(ObjectUtil.isNotEmpty(fileQuery.getFileName()), SysFile::getFileName, fileQuery.getFileName())
                .orderByDesc(SysFile::getId);
    }

    private void checkNotExist(SysFile sysFile) {
        if (ObjectUtil.isNull(sysFile) || DelFlagEnum.Y.getValue().equals(sysFile.getDelFlag())) {
            throw new ServiceException(SysExceptionEnum.FILE_NOT_EXIST);
        }

        String directory = sysFile.getDirectory();
        String fileName = sysFile.getFileName();
        String objectName = directory + File.separator + fileName;
        byte[] bytes = ossHelper.getObject(sysFile.getBucket(), objectName);
        if (ObjectUtil.isNull(bytes) || bytes.length == 0) {
            throw new ServiceException(SysExceptionEnum.FILE_NOT_EXIST);
        }
    }

    private SysFileModel uploadToModel(String bucket, String directory, String original, String fileType, byte[] bytes) {
        String fileName = IdUtil.simpleUUID() + StrUtil.DOT + fileType;
        String objectName = directory + File.separator + fileName;
        boolean flag = ossHelper.putObject(bucket, objectName, bytes, fileType);
        if (flag) {
            Integer expire = BucketEnum.PUBLIC.getName().equals(bucket) ? null : 10 * 60;
            String fileUrl = ossHelper.getObjectUrl(bucket, objectName, expire);
            return SysFileModel.builder().bucket(bucket).directory(directory)
                    .fileName(fileName).fileType(fileType).fileSize(bytes.length)
                    .filePath(fileUrl).original(original).build();
        }
        return null;
    }

}
